import * as Cesium from 'cesium'

/**
 * 坡度坡向分析工具类
 * 提供坡度坡向分析的核心功能，包括算法计算、可视化等
 */
class SlopeAspectUtils {
  /**
   * 构造函数
   * @param {Object} viewer - Cesium Viewer实例
   */
  constructor(viewer) {
    this.viewer = viewer
    this.aspectArrows = []
    this.maxArrowCount = 1000
    this.terrainChangedListener = null
  }

  /**
   * 创建坡度坡向分析材质
   * @param {string} type - 分析类型: 'slope', 'aspect', 'both'
   * @param {number} opacity - 透明度 (0-1)
   * @returns {Cesium.Material} 分析材质
   */
  createAnalysisMaterial(type, opacity = 0.7) {
    try {
      // 注册自定义材质
      if (!Cesium.Material.SlopeAspectType) {
        Cesium.Material.SlopeAspectType = 'SlopeAspect'
        Cesium.Material._materialCache.addMaterial(Cesium.Material.SlopeAspectType, {
          fabric: {
            type: Cesium.Material.SlopeAspectType,
            uniforms: {
              // 使用数字代替字符串来避免GLSL中的字符串比较问题
              // 0 = slope, 1 = aspect, 2 = both
              slopeAspectAnalysisType: 0,
              opacity: opacity
            },
            source: `
              czm_material czm_getMaterial(czm_materialInput materialInput) {
                czm_material material = czm_getDefaultMaterial(materialInput);
                
                // 获取法线
                vec3 normal = normalize(materialInput.normalEC);
                
                // 计算坡度（0-90度）
                float slope = acos(dot(normal, vec3(0.0, 0.0, 1.0))) * 180.0 / 3.14159265359;
                
                // 计算坡向（0-360度）
                float aspect = atan(normal.y, normal.x) * 180.0 / 3.14159265359;
                if (aspect < 0.0) aspect += 360.0;
                
                vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
                
                // 根据分析类型选择颜色
                if (slopeAspectAnalysisType < 0.5) {
                  // 坡度分析
                  if (slope < 10.0) color = mix(vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0), slope / 10.0);
                  else if (slope < 20.0) color = mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.65, 0.0, 1.0), (slope - 10.0) / 10.0);
                  else if (slope < 30.0) color = mix(vec4(1.0, 0.65, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), (slope - 20.0) / 10.0);
                  else if (slope < 45.0) color = mix(vec4(1.0, 0.0, 0.0, 1.0), vec4(0.5, 0.0, 0.5, 1.0), (slope - 30.0) / 15.0);
                  else color = mix(vec4(0.5, 0.0, 0.5, 1.0), vec4(1.0, 1.0, 1.0, 1.0), (slope - 45.0) / 45.0);
                } else if (slopeAspectAnalysisType < 1.5) {
                  // 坡向分析
                  if (aspect < 45.0) color = mix(vec4(1.0, 0.0, 0.0, 1.0), vec4(1.0, 0.65, 0.0, 1.0), aspect / 45.0);
                  else if (aspect < 90.0) color = mix(vec4(1.0, 0.65, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0), (aspect - 45.0) / 45.0);
                  else if (aspect < 135.0) color = mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), (aspect - 90.0) / 45.0);
                  else if (aspect < 180.0) color = mix(vec4(0.0, 1.0, 0.0, 1.0), vec4(0.0, 1.0, 1.0, 1.0), (aspect - 135.0) / 45.0);
                  else if (aspect < 225.0) color = mix(vec4(0.0, 1.0, 1.0, 1.0), vec4(0.0, 0.0, 1.0, 1.0), (aspect - 180.0) / 45.0);
                  else if (aspect < 270.0) color = mix(vec4(0.0, 0.0, 1.0, 1.0), vec4(0.5, 0.0, 0.5, 1.0), (aspect - 225.0) / 45.0);
                  else if (aspect < 315.0) color = mix(vec4(0.5, 0.0, 0.5, 1.0), vec4(1.0, 0.0, 1.0, 1.0), (aspect - 270.0) / 45.0);
                  else color = mix(vec4(1.0, 0.0, 1.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), (aspect - 315.0) / 45.0);
                } else {
                  // 两者同时分析
                  // 坡度颜色映射
                  vec4 slopeColor;
                  if (slope < 10.0) slopeColor = mix(vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0), slope / 10.0);
                  else if (slope < 20.0) slopeColor = mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 0.65, 0.0, 1.0), (slope - 10.0) / 10.0);
                  else if (slope < 30.0) slopeColor = mix(vec4(1.0, 0.65, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), (slope - 20.0) / 10.0);
                  else if (slope < 45.0) slopeColor = mix(vec4(1.0, 0.0, 0.0, 1.0), vec4(0.5, 0.0, 0.5, 1.0), (slope - 30.0) / 15.0);
                  else slopeColor = mix(vec4(0.5, 0.0, 0.5, 1.0), vec4(1.0, 1.0, 1.0, 1.0), (slope - 45.0) / 45.0);
                  
                  // 坡向颜色映射
                  vec4 aspectColor;
                  if (aspect < 45.0) aspectColor = mix(vec4(1.0, 0.0, 0.0, 1.0), vec4(1.0, 0.65, 0.0, 1.0), aspect / 45.0);
                  else if (aspect < 90.0) aspectColor = mix(vec4(1.0, 0.65, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0), (aspect - 45.0) / 45.0);
                  else if (aspect < 135.0) aspectColor = mix(vec4(1.0, 1.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), (aspect - 90.0) / 45.0);
                  else if (aspect < 180.0) aspectColor = mix(vec4(0.0, 1.0, 0.0, 1.0), vec4(0.0, 1.0, 1.0, 1.0), (aspect - 135.0) / 45.0);
                  else if (aspect < 225.0) aspectColor = mix(vec4(0.0, 1.0, 1.0, 1.0), vec4(0.0, 0.0, 1.0, 1.0), (aspect - 180.0) / 45.0);
                  else if (aspect < 270.0) aspectColor = mix(vec4(0.0, 0.0, 1.0, 1.0), vec4(0.5, 0.0, 0.5, 1.0), (aspect - 225.0) / 45.0);
                  else if (aspect < 315.0) aspectColor = mix(vec4(0.5, 0.0, 0.5, 1.0), vec4(1.0, 0.0, 1.0, 1.0), (aspect - 270.0) / 45.0);
                  else aspectColor = mix(vec4(1.0, 0.0, 1.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), (aspect - 315.0) / 45.0);
                  
                  // 混合颜色
                  color = (slopeColor + aspectColor) * 0.5;
                }
                
                color.a = opacity;
                material.diffuse = color.rgb;
                material.alpha = color.a;
                
                return material;
              }
            `
          }
        })
      }

      // 创建材质实例
      const material = new Cesium.Material({
        fabric: {
          type: Cesium.Material.SlopeAspectType,
          uniforms: {
            slopeAspectAnalysisType: type === 'slope' ? 0 : (type === 'aspect' ? 1 : 2),
            opacity: opacity
          }
        }
      })

      return material
    } catch (error) {
      console.error('创建分析材质失败:', error)
      throw error
    }
  }

  /**
   * 创建范围限制的分析材质
   * @param {string} type - 分析类型: 'slope', 'aspect', 'both'
   * @param {Object} rectangle - Cesium.Rectangle对象，表示分析范围
   * @param {number} opacity - 透明度 (0-1)
   * @returns {Cesium.Material} 范围限制的分析材质
   */
  createLimitedAnalysisMaterial(type, rectangle, opacity = 0.7) {
    try {
      // 兼容传入的 rectangle 可能为 Cesium.Rectangle（弧度）或直接以度为单位的对象
      let rectRadians = rectangle
      if (rectangle && typeof rectangle.west === 'number') {
        // 如果值明显大于 2π，认为是度数，需要转换为弧度 Rectangle
        if (Math.abs(rectangle.west) > Math.PI * 2 || Math.abs(rectangle.east) > Math.PI * 2 || Math.abs(rectangle.south) > Math.PI * 2 || Math.abs(rectangle.north) > Math.PI * 2) {
          rectRadians = Cesium.Rectangle.fromDegrees(rectangle.west, rectangle.south, rectangle.east, rectangle.north)
        }
      }
    

    const west = Cesium.Math.toDegrees(rectRadians.west)
    const east = Cesium.Math.toDegrees(rectRadians.east)
    const south = Cesium.Math.toDegrees(rectRadians.south)
    const north = Cesium.Math.toDegrees(rectRadians.north)

    console.log('createLimitedAnalysisMaterial rectRadians (rad):', rectRadians)
    console.log('createLimitedAnalysisMaterial rectDegrees:', { west, south, east, north })
    // 注册范围限制材质类型，避免直接使用通用 'Custom' 导致渲染或缓存问题
    if (!Cesium.Material.SlopeAspectLimitedType) {
      Cesium.Material.SlopeAspectLimitedType = 'SlopeAspectLimited'
      Cesium.Material._materialCache.addMaterial(Cesium.Material.SlopeAspectLimitedType, {
        fabric: {
          type: Cesium.Material.SlopeAspectLimitedType,
          uniforms: {
            analysisTypeValue: 0.0,
            opacity: 0.7
          },
          source: `
              precision highp float;
              uniform float analysisTypeValue;
              uniform float opacity;

              czm_material czm_getMaterial(czm_materialInput materialInput) {
                czm_material material = czm_getDefaultMaterial(materialInput);

                // 使用世界空间法线并归一化，避免因插值精度导致的突变
                vec3 normal = normalize(materialInput.normalEC);

                // 计算坡度与坡向
                float slope = degrees(acos(clamp(dot(normal, vec3(0.0, 0.0, 1.0)), -1.0, 1.0)));
                float aspect = degrees(atan(normal.y, normal.x));
                if (aspect < 0.0) aspect += 360.0;

                vec4 color = vec4(0.0, 0.0, 0.0, 0.0);

                // 用 smoothstep 来平滑边界，提高颜色过渡的连续性
                if (analysisTypeValue < 0.5) {
                  float t1 = smoothstep(0.0, 10.0, slope);
                  float t2 = smoothstep(10.0, 20.0, slope);
                  float t3 = smoothstep(20.0, 30.0, slope);
                  float t4 = smoothstep(30.0, 45.0, slope);
                  vec4 c0 = mix(vec4(0.0,1.0,0.0,1.0), vec4(1.0,1.0,0.0,1.0), t1);
                  vec4 c1 = mix(vec4(1.0,1.0,0.0,1.0), vec4(1.0,0.65,0.0,1.0), t2);
                  vec4 c2 = mix(vec4(1.0,0.65,0.0,1.0), vec4(1.0,0.0,0.0,1.0), t3);
                  vec4 c3 = mix(vec4(1.0,0.0,0.0,1.0), vec4(0.5,0.0,0.5,1.0), t4);
                  color = (c0 + c1 + c2 + c3) / 4.0;
                } else if (analysisTypeValue < 1.5) {
                  float a = mod(aspect, 360.0);
                  // 将360度分为8段，并用平滑插值
                  float seg = 45.0;
                  float idx = floor(a / seg);
                  float localT = fract(a / seg);
                  // 简化：用 HSV 风格分段颜色映射的线性插值
                  vec4 colors[8];
                  colors[0] = vec4(1.0,0.0,0.0,1.0);
                  colors[1] = vec4(1.0,0.65,0.0,1.0);
                  colors[2] = vec4(1.0,1.0,0.0,1.0);
                  colors[3] = vec4(0.0,1.0,0.0,1.0);
                  colors[4] = vec4(0.0,1.0,1.0,1.0);
                  colors[5] = vec4(0.0,0.0,1.0,1.0);
                  colors[6] = vec4(0.5,0.0,0.5,1.0);
                  colors[7] = vec4(1.0,0.0,1.0,1.0);
                  int i0 = int(mod(idx, 8.0));
                  int i1 = int(mod(idx + 1.0, 8.0));
                  vec4 col0 = colors[i0];
                  vec4 col1 = colors[i1];
                  color = mix(col0, col1, smoothstep(0.0, 1.0, localT));
                } else {
                  // both: average slope and aspect colors
                  // 采用较平滑的合成策略避免突变
                  // slope color
                  float t1 = smoothstep(0.0, 10.0, slope);
                  float t2 = smoothstep(10.0, 20.0, slope);
                  float t3 = smoothstep(20.0, 30.0, slope);
                  float t4 = smoothstep(30.0, 45.0, slope);
                  vec4 s0 = mix(vec4(0.0,1.0,0.0,1.0), vec4(1.0,1.0,0.0,1.0), t1);
                  vec4 s1 = mix(vec4(1.0,1.0,0.0,1.0), vec4(1.0,0.65,0.0,1.0), t2);
                  vec4 s2 = mix(vec4(1.0,0.65,0.0,1.0), vec4(1.0,0.0,0.0,1.0), t3);
                  vec4 s3 = mix(vec4(1.0,0.0,0.0,1.0), vec4(0.5,0.0,0.5,1.0), t4);
                  vec4 slopeColor = (s0 + s1 + s2 + s3) / 4.0;

                  // aspect color (reuse palette)
                  float a = mod(aspect, 360.0);
                  float seg = 45.0;
                  float idx = floor(a / seg);
                  float localT = fract(a / seg);
                  vec4 colors[8];
                  colors[0] = vec4(1.0,0.0,0.0,1.0);
                  colors[1] = vec4(1.0,0.65,0.0,1.0);
                  colors[2] = vec4(1.0,1.0,0.0,1.0);
                  colors[3] = vec4(0.0,1.0,0.0,1.0);
                  colors[4] = vec4(0.0,1.0,1.0,1.0);
                  colors[5] = vec4(0.0,0.0,1.0,1.0);
                  colors[6] = vec4(0.5,0.0,0.5,1.0);
                  colors[7] = vec4(1.0,0.0,1.0,1.0);
                  int i0 = int(mod(idx, 8.0));
                  int i1 = int(mod(idx + 1.0, 8.0));
                  vec4 col0 = colors[i0];
                  vec4 col1 = colors[i1];
                  vec4 aspectColor = mix(col0, col1, smoothstep(0.0, 1.0, localT));

                  color = (slopeColor + aspectColor) * 0.5;
                }

                color.a = opacity;
                material.diffuse = color.rgb;
                material.alpha = color.a;
                return material;
              }
            `
        }
      })
    }

    // 从已注册的材质类型创建实例并设置统一参数
    const limitedMaterial = new Cesium.Material({
      fabric: {
        type: Cesium.Material.SlopeAspectLimitedType,
        uniforms: {
          rectangleWest: west,
          rectangleEast: east,
          rectangleSouth: south,
          rectangleNorth: north,
          analysisTypeValue: type === 'slope' ? 0.0 : (type === 'aspect' ? 1.0 : 2.0),
          opacity: opacity
        }
      }
    })

    // 创建一个覆盖指定矩形的 Primitive 来承载该材质，避免将材质直接赋给 globe.material（globe.material 会基于已加载的瓦片导致显示范围与视图相关）
    try {
      // 清除已有的同类 primitive
      if (this.limitedAnalysisPrimitive) {
        try { this.viewer.scene.primitives.remove(this.limitedAnalysisPrimitive) } catch (e) { /* ignore */ }
        this.limitedAnalysisPrimitive = null
      }

      const instance = new Cesium.GeometryInstance({
        // 为了避免在大三角面上出现明显的面片（triangulation faceting），根据范围尺寸动态增加细分
        geometry: new Cesium.RectangleGeometry({
          rectangle: rectRadians,
          vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,
          // 计算合适的 granularity，使较小范围拥有更多细分，从而获得平滑的法线与颜色过渡
          // 目标将最大边分成约 targetSegments 段，避免过大或过小
          granularity: (function() {
            try {
              const lonSpan = Math.abs(rectRadians.east - rectRadians.west);
              const latSpan = Math.abs(rectRadians.north - rectRadians.south);
              const maxSpan = Math.max(lonSpan, latSpan);
              const targetSegments = 160; // 目标细分数，可根据性能/效果权衡调整
              const minGranularity = Cesium.Math.toRadians(0.0005); // 最小细分（约0.0005度）
              const computed = maxSpan / targetSegments;
              return Math.max(Math.min(computed, maxSpan), minGranularity);
            } catch (e) {
              return Cesium.Math.toRadians(0.0005);
            }
          })()
        })
      })

      // 使用 GroundPrimitive 使矩形贴合地形，从而能使用地形法线进行坡度/坡向计算
        const groundPrimitive = new Cesium.GroundPrimitive({
          geometryInstances: instance,
          appearance: new Cesium.MaterialAppearance({
            material: limitedMaterial,
            // 不强制 faceForward，使用真实地形法线供着色器计算
            faceForward: false,
            translucent: true,
            // 确保使用默认的渲染状态以获得正确的透明合成
            renderState: {
              depthTest: { enabled: true },
              blending: Cesium.BlendingState.ALPHA_BLEND
            }
          }),
          asynchronous: false,
          // 在构造时设置 allowPicking，避免运行时设置只读属性导致错误
          allowPicking: false
        })

      try {
        // 强制清除 globe.material，避免旧的材质覆盖显示
        try { this.viewer.scene.globe.material = undefined } catch (e) { /* ignore */ }

        this.viewer.scene.primitives.add(groundPrimitive)
        this.limitedAnalysisPrimitive = groundPrimitive

        // 将 primitive 提升到最上层以便观察
        try { this.viewer.scene.primitives.raiseToTop(groundPrimitive) } catch (e) { /* ignore */ }

        console.log('已添加范围限制 GroundPrimitive，primitives count:', this.viewer.scene.primitives.length)
        try {
          const rectDeg = {
            west: Cesium.Math.toDegrees(rectRadians.west),
            south: Cesium.Math.toDegrees(rectRadians.south),
            east: Cesium.Math.toDegrees(rectRadians.east),
            north: Cesium.Math.toDegrees(rectRadians.north)
          }
          console.log('GroundPrimitive 覆盖矩形（度）:', rectDeg)
        } catch (e) {
          // ignore
        }
        // 请求渲染以确保新 primitive 生效
        try { this.viewer.scene.requestRender() } catch (e) { /* ignore */ }
        // 打印 globe.material 状态供调试
        try { console.log('当前 globe.material:', this.viewer.scene.globe.material) } catch (e) { /* ignore */ }
      } catch (primitiveError) {
        console.warn('创建范围限制的分析 primitive 失败，回退只返回材质:', primitiveError)
      }

      return limitedMaterial
    } catch (error) {
      console.error('创建范围限制的分析材质失败:', error)
      throw error
    }
  } catch (error) {
      console.error('创建范围限制的分析材质失败:', error)
      throw error
    }
  }


  /**
   * 创建坡向箭头
   * @param {Object} options - 配置选项
   * @param {string} options.extentType - 范围类型: 'global', 'view', 'custom'
   * @param {Object} [options.customExtent] - 自定义范围，当extentType为'custom'时必须提供
   * @param {number} [options.gridSize=30] - 箭头网格大小
   * @param {number} [options.scale=0.5] - 箭头缩放因子
   * @param {number} [options.heightOffset=3] - 箭头距离地面的高度偏移
   * @returns {Promise<Array>} 创建的箭头实体数组
   */
  async createAspectArrows(options = {}) {
    if (!this.viewer) {
      throw new Error('Viewer实例未提供')
    }
    try {
      const {
        extentType = 'view',
        customExtent = null,
        gridSize = 30,
        scale = 0.5,
        heightOffset = 3
      } = options

      // 清除已有的箭头
      this.clearAspectArrows()
      if (!this.aspectArrows) this.aspectArrows = []

      // 确定分析范围
      let rectangle = null
      if (extentType === 'global') {
        const cameraPosition = this.viewer.camera.positionCartographic
        const centerLon = Cesium.Math.toDegrees(cameraPosition.longitude)
        const centerLat = Cesium.Math.toDegrees(cameraPosition.latitude)
        rectangle = Cesium.Rectangle.fromDegrees(
          centerLon - 5,
          centerLat - 5,
          centerLon + 5,
          centerLat + 5
        )
      } else if (extentType === 'view') {
        rectangle = this.viewer.camera.computeViewRectangle()
        if (!rectangle) {
          const cameraPosition = this.viewer.camera.positionCartographic
          const centerLon = Cesium.Math.toDegrees(cameraPosition.longitude)
          const centerLat = Cesium.Math.toDegrees(cameraPosition.latitude)
          rectangle = Cesium.Rectangle.fromDegrees(
            centerLon - 5,
            centerLat - 5,
            centerLon + 5,
            centerLat + 5
          )
        }
      } else if (extentType === 'custom' && customExtent) {
        rectangle = customExtent
      } else {
        throw new Error('无效的范围类型或未提供自定义范围')
      }

      // 计算网格采样点
      const west = Cesium.Math.toDegrees(rectangle.west)
      const east = Cesium.Math.toDegrees(rectangle.east)
      const south = Cesium.Math.toDegrees(rectangle.south)
      const north = Cesium.Math.toDegrees(rectangle.north)
      // 自适应采样点数：面积小则点少，面积大则点多
      let area = Math.abs((east - west) * (north - south)); // 单位：度^2
      // 经验公式：每1度^2 9点，最小3x3，最大this.maxArrowCount
      let gridN = 3;
      if (area > 0.01) {
        gridN = Math.ceil(Math.sqrt(Math.min(this.maxArrowCount, Math.max(9, area * 9))));
      }
      gridN = Math.max(3, Math.min(gridN, 50));
      const samplePoints = [];
      for (let i = 0; i < gridN; i++) {
        for (let j = 0; j < gridN; j++) {
          const lon = west + (east - west) * (i / (gridN - 1));
          const lat = south + (north - south) * (j / (gridN - 1));
          samplePoints.push(Cesium.Cartographic.fromDegrees(lon, lat));
          if (samplePoints.length >= this.maxArrowCount) break;
        }
        if (samplePoints.length >= this.maxArrowCount) break;
      }
      if (samplePoints.length === 0) {
        console.warn('没有创建任何箭头点')
        return this.aspectArrows
      }
      // 批量采样地形高度
      const sampledPositions = await Cesium.sampleTerrainMostDetailed(this.viewer.terrainProvider, samplePoints)
      // 批量获取坡向和坡度
      const aspectSlopePromises = sampledPositions.map(position => this.computeNormalAndAspect(position));
      const aspectSlopeResults = await Promise.all(aspectSlopePromises);
      // 为每个点创建 entity 箭头，严格贴地
      for (let i = 0; i < sampledPositions.length; i++) {
        const position = sampledPositions[i]
        const { aspect, slope } = aspectSlopeResults[i]
        try {
          if (position && !isNaN(position.height) && slope > 5) {
            // entity 方式，严格贴地
            const arrowEntity = this.viewer.entities.add({
              name: '坡向箭头',
              position: Cesium.Cartesian3.fromRadians(position.longitude, position.latitude, position.height),
              billboard: {
                image: this.createArrowImage(aspect, slope),
                scale: scale,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
                pixelOffset: new Cesium.Cartesian2(0, -5),
                eyeOffset: new Cesium.Cartesian3(0, 0, -100),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
              }
            })
            this.aspectArrows.push(arrowEntity)
          }
        } catch (innerError) {
          console.warn('创建箭头失败:', innerError)
          continue
        }
      }
      return this.aspectArrows
    } catch (error) {
      console.error('创建坡向箭头失败:', error)
      throw error
    }
  }

  /**
   * 计算点的法线和坡向
   * @param {Object} position - Cesium.Cartographic位置对象
   * @returns {Promise<Object>} 包含aspect和slope的对象
   */
  async computeNormalAndAspect(position) {
    try {
      // 获取周围的点来计算法线
      const lon = Cesium.Math.toDegrees(position.longitude)
      const lat = Cesium.Math.toDegrees(position.latitude)
      const height = position.height || 0

      // 在点周围创建四个点用于计算法线
      const neighborPoints = [
        Cesium.Cartographic.fromDegrees(lon + 0.001, lat, 0),
        Cesium.Cartographic.fromDegrees(lon - 0.001, lat, 0),
        Cesium.Cartographic.fromDegrees(lon, lat + 0.001, 0),
        Cesium.Cartographic.fromDegrees(lon, lat - 0.001, 0)
      ]

      // 获取周围点的高度
      const sampledNeighbors = await Cesium.sampleTerrainMostDetailed(this.viewer.terrainProvider, neighborPoints)

      if (sampledNeighbors.length >= 4) {
        // 计算法线向量
        const eastHeight = sampledNeighbors[0].height || 0
        const westHeight = sampledNeighbors[1].height || 0
        const northHeight = sampledNeighbors[2].height || 0
        const southHeight = sampledNeighbors[3].height || 0

        // 计算梯度
        const dx = (eastHeight - westHeight) / 0.002
        const dy = (northHeight - southHeight) / 0.002

        // 计算法线
        const normal = new Cesium.Cartesian3(-dx, -dy, 1)
        Cesium.Cartesian3.normalize(normal, normal)

        // 计算坡度
        const slope = Math.acos(Math.min(1, Math.max(0, normal.z))) * 180 / Math.PI

        // 计算坡向
        let aspect = Math.atan2(normal.y, normal.x) * 180 / Math.PI
        if (aspect < 0) aspect += 360

        return { aspect, slope }
      } else {
        // 如果无法获取周围点的高度，使用随机值作为备用
        return {
          aspect: Math.random() * 360,
          slope: 10 + Math.random() * 30
        }
      }
    } catch (error) {
      console.warn('计算法线和坡向失败，使用随机值:', error)
      // 出错时使用随机值
      return {
        aspect: Math.random() * 360,
        slope: 10 + Math.random() * 30
      }
    }
  }

  /**
   * 初始化箭头公告板集合
   */
  initArrowBillboardCollection() {
    if (!this.arrowBillboardCollection) {
      this.arrowBillboardCollection = new Cesium.BillboardCollection({
        scene: this.viewer.scene,
        blendOption: Cesium.BlendOption.OPAQUE_AND_TRANSLUCENT
      });
      this.viewer.scene.primitives.add(this.arrowBillboardCollection);
    }
  }

  /**
   * 创建箭头精灵图
   */
  createArrowSpriteSheet() {
    // 预生成16个方向的箭头精灵图
    const canvas = document.createElement('canvas');
    const size = 32;
    const directions = 16;
    canvas.width = size * directions;
    canvas.height = size;
    const ctx = canvas.getContext('2d');

    for (let i = 0; i < directions; i++) {
      const angle = (i * (360 / directions) - 90) * Math.PI / 180;
      ctx.save();
      ctx.translate(i * size + size / 2, size / 2);
      ctx.rotate(angle);

      // 绘制箭头
      ctx.beginPath();
      ctx.moveTo(0, -10);
      ctx.lineTo(-4, 0);
      ctx.lineTo(0, -3);
      ctx.lineTo(4, 0);
      ctx.closePath();
      ctx.fillStyle = 'rgba(255, 255, 255, 1)';
      ctx.fill();
      ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
      ctx.lineWidth = 1;
      ctx.stroke();
      ctx.restore();
    }

    return canvas.toDataURL('image/png');
  }

  /**
   * 初始化箭头资源
   */
  async initArrowResources() {
    if (!this.arrowSpriteSheet) {
      this.arrowSpriteSheet = this.createArrowSpriteSheet();
      this.initArrowBillboardCollection();
    }
  }

  /**
   * 添加箭头到公告板集合
   * @param {Object} position - Cesium.Cartographic位置对象
   * @param {number} aspect - 坡向角度 (0-360)
   * @param {number} slope - 坡度角度 (0-90)
   * @param {number} scale - 缩放因子
   * @param {number} heightOffset - 高度偏移
   */
  addArrowToCollection(position, aspect, slope, scale = 0.5, heightOffset = 3) {
    try {
      // 根据坡度确定箭头大小和透明度
      const arrowSize = Math.max(0.3, Math.min(1.0, slope / 45)) * scale;
      const alpha = Math.min(1, slope / 45);

      // 直接使用createArrowImage为每个箭头创建独立的图像
      // 这样可以避免精灵图纹理坐标计算和WebGL纹理创建的问题
      const arrowImage = this.createArrowImage(aspect, slope, alpha);

      // 添加到公告板集合
      this.arrowBillboardCollection.add({
        position: Cesium.Cartesian3.fromRadians(position.longitude, position.latitude, (position.height || 0) + heightOffset),
        image: arrowImage,
        scale: arrowSize,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        disableDepthTestDistance: Number.POSITIVE_INFINITY,
        pixelOffset: new Cesium.Cartesian2(0, -5),
        eyeOffset: new Cesium.Cartesian3(0, 0, -100),
        alignedAxis: new Cesium.Cartesian3(0, 0, 1)
      });
    } catch (arrowError) {
      console.warn('创建箭头实体失败:', arrowError)
      return null
    }
  }

  /**
   * 创建箭头图片
   * @param {number} aspect - 坡向角度 (0-360)
   * @param {number} slope - 坡度角度 (0-90)
   * @param {number} opacity - 透明度 (0-1)
   * @returns {string} 箭头图片的data URL
   */
  createArrowImage(aspect, slope, opacity = 1.0) {
    try {
      // 创建画布
      const canvas = document.createElement('canvas')
      canvas.width = 50
      canvas.height = 50
      const ctx = canvas.getContext('2d')

      // 设置全局alpha
      ctx.globalAlpha = Math.min(1, opacity)

      // 将原点移到画布中心
      ctx.translate(25, 25)

      // 旋转画布以匹配坡向
      ctx.rotate((aspect - 90) * Math.PI / 180) // 调整旋转方向以匹配实际坡向

      // 根据坡度调整箭头颜色和长度
      const alpha = Math.min(1, slope / 45)
      const length = 20 + (slope / 90) * 15 // 增加箭头长度以提高可见性
      const width = 8 // 增加箭头宽度

      // 绘制箭头（更粗更明显）
      ctx.beginPath()
      ctx.moveTo(0, -length)
      ctx.lineTo(-width, 0)
      ctx.lineTo(0, -8)
      ctx.lineTo(width, 0)
      ctx.closePath()

      // 设置箭头颜色
      let r, g, b
      if (aspect < 45) { // 北
        r = 255; g = 0; b = 0
      } else if (aspect < 90) { // 东北
        r = 255; g = 165; b = 0
      } else if (aspect < 135) { // 东
        r = 255; g = 255; b = 0
      } else if (aspect < 180) { // 东南
        r = 0; g = 255; b = 0
      } else if (aspect < 225) { // 南
        r = 0; g = 255; b = 255
      } else if (aspect < 270) { // 西南
        r = 0; g = 0; b = 255
      } else if (aspect < 315) { // 西
        r = 128; g = 0; b = 128
      } else { // 西北
        r = 255; g = 0; b = 255
      }

      // 主箭头颜色
      ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha * 1.0})`
      ctx.fill()

      // 添加白色边框以提高可见性
      ctx.strokeStyle = 'rgba(255, 255, 255, 0.9)'
      ctx.lineWidth = 2
      ctx.stroke()

      // 绘制中心白色线条以增强视觉效果
      ctx.beginPath()
      ctx.moveTo(0, -length + 5)
      ctx.lineTo(0, 0)
      ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)'
      ctx.lineWidth = 1
      ctx.stroke()

      // 将canvas转换为data URL
      const dataUrl = canvas.toDataURL('image/png')

      // 重置全局alpha
      ctx.globalAlpha = 1.0

      return dataUrl
    } catch (e) {
      console.error('创建箭头图片失败:', e)
      // 返回备用图片
      const canvas = document.createElement('canvas')
      canvas.width = 20
      canvas.height = 20
      const ctx = canvas.getContext('2d')
      ctx.fillStyle = 'rgba(255, 0, 0, 0.8)'
      ctx.fillRect(0, 0, 20, 20)
      return canvas.toDataURL('image/png')
    }
  }

  /**
   * 清除所有坡向箭头
   */
  clearAspectArrows() {
    if (this.arrowBillboardCollection) {
      this.arrowBillboardCollection.removeAll();
    }
    if (this.viewer && this.aspectArrows.length > 0) {
      this.aspectArrows.forEach(arrow => {
        this.viewer.entities.remove(arrow)
      })
      this.aspectArrows = []
    }
  }

  /**
   * 更新分析透明度
   * @param {number} opacity - 透明度值 (0-1)
   * @param {Object} [materials] - 材质对象集合
   */
  updateOpacity(opacity, materials = {}) {
    if (!this.viewer) return

    // 更新各个材质的透明度
    if (materials.slopeMaterial) {
      materials.slopeMaterial.uniforms.opacity = opacity
    }
    if (materials.aspectMaterial) {
      materials.aspectMaterial.uniforms.opacity = opacity
    }
    if (materials.bothMaterial) {
      materials.bothMaterial.uniforms.opacity = opacity
    }

    // 触发重新渲染
    this.viewer.scene.requestRender()
  }

  /**
   * 创建图例
   * @param {HTMLElement} container - 图例容器元素
   * @param {string} analysisType - 分析类型: 'slope', 'aspect', 'both'
   * @param {string} [aspectRepresentation='color'] - 坡向表示方式: 'color' 或 'arrow'
   */
  createLegend(container, analysisType, aspectRepresentation = 'color') {
    if (!container) return

    // 清空容器
    container.innerHTML = ''

    // 坡度颜色映射
    const slopeColors = [
      { color: '#00FF00', label: '0-10°' },
      { color: '#FFFF00', label: '10-20°' },
      { color: '#FFA500', label: '20-30°' },
      { color: '#FF0000', label: '30-45°' },
      { color: '#800080', label: '45-60°' },
      { color: '#FFFFFF', label: '60-90°' }
    ]

    // 坡向颜色映射
    const aspectColors = [
      { color: '#FF0000', label: '北' },
      { color: '#FFA500', label: '东北' },
      { color: '#FFFF00', label: '东' },
      { color: '#00FF00', label: '东南' },
      { color: '#00FFFF', label: '南' },
      { color: '#0000FF', label: '西南' },
      { color: '#800080', label: '西' },
      { color: '#FF00FF', label: '西北' }
    ]

    // 创建图例项函数
    const createLegendItem = (color, label) => {
      const item = document.createElement('div')
      item.className = 'legend-item'

      const colorBox = document.createElement('div')
      colorBox.className = 'legend-color'
      colorBox.style.backgroundColor = color

      const labelText = document.createElement('span')
      labelText.className = 'legend-label'
      labelText.textContent = label

      item.appendChild(colorBox)
      item.appendChild(labelText)

      return item
    }

    // 创建箭头图例HTML
    const createArrowLegendHtml = () => {
      return `
        <div class="arrow-legend">
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(270deg)"></div>
            <span>北</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(315deg)"></div>
            <span>东北</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(0deg)"></div>
            <span>东</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(45deg)"></div>
            <span>东南</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(90deg)"></div>
            <span>南</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(135deg)"></div>
            <span>西南</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(180deg)"></div>
            <span>西</span>
          </div>
          <div class="arrow-legend-item">
            <div class="arrow-legend-icon" style="transform: rotate(225deg)"></div>
            <span>西北</span>
          </div>
        </div>
      `
    }

    // 根据分析类型创建图例
    if (analysisType === 'slope') {
      const slopeLegend = document.createElement('div')
      slopeLegend.className = 'slope-legend legend-section'

      slopeColors.forEach(item => {
        slopeLegend.appendChild(createLegendItem(item.color, item.label))
      })

      container.appendChild(slopeLegend)
    } else if (analysisType === 'aspect') {
      const aspectLegend = document.createElement('div')
      aspectLegend.className = 'aspect-legend legend-section'

      if (aspectRepresentation === 'color') {
        aspectColors.forEach(item => {
          aspectLegend.appendChild(createLegendItem(item.color, item.label))
        })
      } else if (aspectRepresentation === 'arrow') {
        // 箭头图例
        const arrowLegendContent = document.createElement('div');
        arrowLegendContent.innerHTML = createArrowLegendHtml();
        // 将箭头图例项直接添加到aspectLegend，移除额外容器
        const arrowItems = arrowLegendContent.querySelectorAll('.arrow-legend-item');
        arrowItems.forEach(item => aspectLegend.appendChild(item));
      }

      container.appendChild(aspectLegend)
    } else if (analysisType === 'both') {
      // 同时显示坡度和坡向图例
      const bothLegend = document.createElement('div')
      bothLegend.className = 'both-legend'

      // 坡度图例
      const slopeLegendSection = document.createElement('div')
      slopeLegendSection.className = 'legend-section'

      const slopeLegendHeader = document.createElement('h5')
      slopeLegendHeader.textContent = '坡度'
      slopeLegendSection.appendChild(slopeLegendHeader)

      const slopeLegend = document.createElement('div')
      slopeLegend.className = 'slope-legend'

      slopeColors.forEach(item => {
        slopeLegend.appendChild(createLegendItem(item.color, item.label))
      })

      slopeLegendSection.appendChild(slopeLegend)
      bothLegend.appendChild(slopeLegendSection)

      // 坡向图例
      const aspectLegendSection = document.createElement('div')
      aspectLegendSection.className = 'legend-section'

      const aspectLegendHeader = document.createElement('h5')
      aspectLegendHeader.textContent = '坡向'
      aspectLegendSection.appendChild(aspectLegendHeader)

      const aspectLegend = document.createElement('div')
      aspectLegend.className = 'aspect-legend'

      if (aspectRepresentation === 'color') {
        aspectColors.forEach(item => {
          aspectLegend.appendChild(createLegendItem(item.color, item.label))
        })
      } else if (aspectRepresentation === 'arrow') {
        // 箭头图例
        const arrowLegend = document.createElement('div')
        arrowLegend.className = 'arrow-legend legend-two-column'
        arrowLegend.innerHTML = createArrowLegendHtml()

        aspectLegend.appendChild(arrowLegend)
      }

      aspectLegendSection.appendChild(aspectLegend)
      bothLegend.appendChild(aspectLegendSection)

      container.appendChild(bothLegend)
    }
  }

  /**
   * 应用坡度坡向分析
   * @param {Object} options - 分析选项
   * @param {string} options.analysisType - 分析类型: 'slope', 'aspect', 'both'
   * @param {string} [options.extentType='view'] - 分析范围类型: 'global', 'view', 'custom'
   * @param {Object} [options.customExtent=null] - 自定义范围
   * @param {number} [options.opacity=0.7] - 透明度
   * @param {string} [options.aspectRepresentation='color'] - 坡向表示方式: 'color' 或 'arrow'
   * @returns {Promise<Object>} 包含应用的材质引用
   */
  async applyAnalysis(options = {}) {
    if (!this.viewer) {
      throw new Error('Viewer实例未提供')
    }

    try {
      const {
        analysisType = 'slope',
        extentType = 'view',
        customExtent = null,
        opacity = 0.7,
        aspectRepresentation = 'color'
      } = options

      // 确保地形已启用
      if (!this.viewer.terrainProvider || this.viewer.terrainProvider.constructor.name === 'EllipsoidTerrainProvider') {
        throw new Error('请先启用地形显示')
      }

      console.log('applyAnalysis 接收到 options:', options)
      // 用于存储材质引用
      const materials = {}

      // 如果是坡向箭头表示，创建箭头
      if ((analysisType === 'aspect' || analysisType === 'both') && aspectRepresentation === 'arrow') {
        // 对于both类型，如果使用箭头表示坡向，仍需应用坡度材质
        if (analysisType === 'both') {
          // 创建坡度材质
          materials.slopeMaterial = this.createAnalysisMaterial('slope', opacity)

          // 根据不同的分析范围应用材质：对于自定义范围，我们使用 rectangle primitive（在 createLimitedAnalysisMaterial 中已创建），否则直接赋给 globe.material
          if (extentType === 'custom' && customExtent) {
            this.createLimitedAnalysisMaterial('slope', customExtent, opacity)
            // 确保 globe.material 恢复默认，不遮挡 primitive
            this.viewer.scene.globe.material = undefined
            console.log('创建自定义范围材质后，limitedAnalysisPrimitive:', !!this.limitedAnalysisPrimitive)
          } else {
            // 全局或视图范围的坡度材质
            this.viewer.scene.globe.material = materials.slopeMaterial
          }
        } else {
          // 纯坡向分析，清除globe材质
          this.viewer.scene.globe.material = undefined
        }

        // 初始化箭头资源
        await this.initArrowResources();
        // 清空现有箭头
        this.clearAspectArrows();
        // 创建坡向箭头
        await this.createAspectArrows({
          extentType,
          customExtent,
          gridSize: 40,
          scale: 0.5,
          heightOffset: 3
        })

        // 强制刷新场景
        this.viewer.scene.requestRender()
      } else {
        // 颜色表示方式的处理逻辑
        // 创建分析材质
        if (analysisType === 'slope' || analysisType === 'both') {
          materials.slopeMaterial = this.createAnalysisMaterial('slope', opacity)
        }

        if (analysisType === 'aspect' || analysisType === 'both') {
          materials.aspectMaterial = this.createAnalysisMaterial('aspect', opacity)
        }

        // 创建分析材质
        let materialToApply = null
        if (analysisType === 'slope') {
          materialToApply = materials.slopeMaterial
        } else if (analysisType === 'aspect') {
          materialToApply = materials.aspectMaterial
        } else if (analysisType === 'both') {
          // 同时显示坡度和坡向时，创建一个组合材质
          materials.bothMaterial = this.createAnalysisMaterial('both', opacity)
          materialToApply = materials.bothMaterial
        }

        // 根据不同的分析范围应用材质
        if (extentType === 'custom' && customExtent) {
          // 自定义范围 - 在 createLimitedAnalysisMaterial 中会创建一个覆盖 rectangle 的 primitive
          this.createLimitedAnalysisMaterial(analysisType, customExtent, opacity)
          // 确保 globe.material 恢复默认（primitive 覆盖在 globe 之上显示）
          this.viewer.scene.globe.material = undefined
          console.log('创建自定义范围材质后，limitedAnalysisPrimitive:', !!this.limitedAnalysisPrimitive)
        } else {
          // 默认全局或视图范围
          this.viewer.scene.globe.material = materialToApply
        }
      }

      // 先移除之前可能存在的监听器，再添加新的地形变化监听器
      if (this.terrainChangedListener) {
        this.viewer.scene.globe.tileLoadProgressEvent.removeEventListener(this.terrainChangedListener)
      }
      this.terrainChangedListener = () => {
        // 只在地形瓦片加载完成后才刷新分析，避免频繁闪烁
        if (this.viewer.scene.globe._tileLoadQueueLength === 0) {
          this.applyAnalysis(options)
        }
      }
      this.viewer.scene.globe.tileLoadProgressEvent.addEventListener(this.terrainChangedListener)

      console.log('坡度坡向分析已应用')
      return materials
    } catch (error) {
      console.error('应用坡度坡向分析失败:', error)
      throw error
    }
  }

  /**
   * 清除所有分析结果
   */
  clearAnalysis() {
    if (this.viewer) {
      // 移除自定义材质，让Cesium恢复到默认的影像图层显示
      this.viewer.scene.globe.material = undefined

      // 移除可能存在的范围限制 primitive
      if (this.limitedAnalysisPrimitive) {
        try {
          this.viewer.scene.primitives.remove(this.limitedAnalysisPrimitive)
        } catch (e) {
          console.warn('移除范围限制 primitive 失败:', e)
        }
        this.limitedAnalysisPrimitive = null
      }

      // 移除地形变化监听器
      if (this.terrainChangedListener) {
        this.viewer.scene.globe.tileLoadProgressEvent.removeEventListener(this.terrainChangedListener)
        this.terrainChangedListener = null
      }

      // 清除坡向箭头
      this.clearAspectArrows()
    }
  }

  /**
   * 在视图中心创建一个测试箭头
   * @param {number} [scale=1.5] - 箭头缩放因子
   * @returns {Promise<Object>} 创建的测试箭头实体
   */
  async createTestArrow(scale = 1.5) {
    if (!this.viewer) {
      throw new Error('Viewer实例未提供')
    }

    try {
      console.log('开始创建测试箭头...')
      // 获取视图中心位置
      const center = this.viewer.camera.pickEllipsoid(new Cesium.Cartesian2(
        this.viewer.canvas.clientWidth / 2,
        this.viewer.canvas.clientHeight / 2
      ))

      if (!center) {
        console.warn('无法获取视图中心位置，尝试使用相机位置')
        // 回退到相机位置
        const cameraPosition = this.viewer.camera.positionCartographic
        const cartographic = Cesium.Cartographic.fromRadians(
          cameraPosition.longitude,
          cameraPosition.latitude,
          0
        )

        // 创建测试箭头
        const testArrow = this.viewer.entities.add({
          name: '测试箭头',
          position: Cesium.Cartesian3.fromRadians(
            cartographic.longitude,
            cartographic.latitude,
            100
          ),
          billboard: {
            image: this.createArrowImage(45, 45), // 东北方向，45度坡度
            scale: scale,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            disableDepthTestDistance: Number.POSITIVE_INFINITY,
            pixelOffset: new Cesium.Cartesian2(0, -5),
            eyeOffset: new Cesium.Cartesian3(0, 0, -100)
          }
        })

        this.aspectArrows.push(testArrow)
        console.log('已创建测试箭头（基于相机位置）')
        return testArrow
      }

      const cartographic = Cesium.Cartographic.fromCartesian(center)
      const lon = Cesium.Math.toDegrees(cartographic.longitude)
      const lat = Cesium.Math.toDegrees(cartographic.latitude)

      try {
        // 获取地形高度
        const positions = [Cesium.Cartographic.fromDegrees(lon, lat)]
        const sampledPositions = await Cesium.sampleTerrainMostDetailed(
          this.viewer.terrainProvider,
          positions
        )

        if (sampledPositions && sampledPositions.length > 0) {
          const position = sampledPositions[0]
          const height = position.height || 0

          // 创建一个大的、明显的测试箭头
          const testArrow = this.viewer.entities.add({
            name: '测试箭头',
            position: Cesium.Cartesian3.fromRadians(
              position.longitude,
              position.latitude,
              height + 10
            ),
            billboard: {
              image: this.createArrowImage(45, 45), // 东北方向，45度坡度
              scale: scale,
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
              disableDepthTestDistance: Number.POSITIVE_INFINITY,
              pixelOffset: new Cesium.Cartesian2(0, -5),
              eyeOffset: new Cesium.Cartesian3(0, 0, -100)
            }
          })

          this.aspectArrows.push(testArrow)
          console.log('已创建测试箭头，位置:', { lon, lat, height })

          // 聚焦到测试箭头
          this.viewer.camera.flyTo({
            destination: Cesium.Cartesian3.fromRadians(
              position.longitude,
              position.latitude,
              height + 500
            ),
            duration: 1.5
          })

          return testArrow
        }
      } catch (heightError) {
        console.warn('获取地形高度失败，使用默认高度:', heightError)
        // 使用默认高度创建箭头
        const testArrow = this.viewer.entities.add({
          name: '测试箭头',
          position: Cesium.Cartesian3.fromRadians(
            cartographic.longitude,
            cartographic.latitude,
            100
          ),
          billboard: {
            image: this.createArrowImage(45, 45),
            scale: scale,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            disableDepthTestDistance: Number.POSITIVE_INFINITY
          }
        })
        this.aspectArrows.push(testArrow)
        return testArrow
      }
    } catch (error) {
      console.error('创建测试箭头失败:', error)
      throw error
    }
  }

  /**
   * 销毁工具实例，清理资源
   */
  destroy() {
    this.clearAnalysis()
    this.aspectArrows = []
    this.terrainChangedListener = null
    this.viewer = null
  }
}

export default SlopeAspectUtils